package com.db.main;

import android.text.TextUtils;
import com.db.adjust.ActivityPackage;
import com.db.adjust.PackageBuilder;
import com.db.adjust.ResponseData;
import com.db.adjust.UtilNetworking;
import com.db.env.Machine;
import com.db.env.ReferrerDetails;
import com.db.utils.*;
import com.db.utils.okhttp.HttpClientUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.db.main.Constant.ALIVE_URL;

public class Task implements Runnable {


    private static final Log log = LogFactory.getLog(Task.class);
    protected JSONObject taskJson;

    public Task(JSONObject taskJson) {
        this.taskJson = taskJson;
    }

    @Override
    public void run() {
        String uuid = Util.createUuid();
        String clickId = Util.createUuid();

        // 是否上传信息用于留存业务
        String keeps = taskJson.get("keeps") == JSONObject.NULL ? "" : (String) taskJson.get("keeps");

        // 包Info
        int taskId = taskJson.getInt("id");
        String packageName = taskJson.getString("packageName");
        if (packageName == null || "null".equals(packageName)) {
            LogUtil.logApp("Android Task Exception", packageName + " is null or empty" + ", packageName=" + packageName);
            return;
        }
        AppInfoUtil.AppInfo appInfoObj = AppInfoUtil.getAppInfo(packageName, "Android");
        if (appInfoObj == null) {
            LogUtil.logApp("AppInfo Exception", packageName + " appInfo not found");
            return;
        }
        String appInfo = appInfoObj.appInfo;
        String algorithm = appInfoObj.algorithm;
        String signatureMeta = appInfoObj.signatureMeta;
        String programme = appInfoObj.programme;
        String way = appInfoObj.way;
        String campaign = appInfoObj.campaign;
        String adgroup = appInfoObj.adgroup;
        String creative = appInfoObj.creative;
        String callback = appInfoObj.callback;
        LogUtil.logApp("AppInfo", MessageFormat.format("adjust-sdk: {0}, program:{1}", way, programme));
        LogUtil.logApp("AppInfo", MessageFormat.format("uuid:{0}, appInfo: {1}", uuid, appInfo.trim()));

        JSONObject appJson = new JSONObject(appInfo);
        String infoPackageName = appJson.getString("packageName");
        if (!packageName.equals(infoPackageName)) {
            LogUtil.logApp("AppInfo Exception", "packageName wrong in app info!");
            return;
        }
        // 任务国家iso
        String countryStr = taskJson.getString("countries");
        String[] countries = countryStr.split(",");
        if (countries.length == 0) {
            LogUtil.logApp("Task Exception", "No Countries!");
            return;
        }
        String iso = countries[RandomUtil.getRange(0, countries.length)];
        iso = iso.trim();
        LogUtil.logApp("AppInfo", MessageFormat.format("uuid: {0}, countries: {1}, iso: {2}", uuid, countryStr, iso));

        // 代理运营商，默认用h5_site_01_18（kkoip）、h5_site_51（ipidea）、ip_feng6（ppfly）
        String ipProvider = taskJson.get("ipProvider") != null ? taskJson.get("ipProvider").toString() : "ip_feng6";
        // 如果iso属于HK、TW，则使用ip-idea
//        if ("HK".equalsIgnoreCase(iso) || "TW".equalsIgnoreCase(iso)) {
//            ipProvider = "h5_site_51";
//        }

        // 代理测试
        LogUtil.logApp("ProxyProvider", "代理ip供应商：" + ipProvider);

        String ipWeight = taskJson.getString("ipWeight");
        String clickURL = taskJson.getString("offerShortUrl");

        // CTIT
        int minCtit = taskJson.get("minCtit") == JSONObject.NULL ? 20 : taskJson.getInt("minCtit");
        int maxCtit = taskJson.get("maxCtit") == JSONObject.NULL ? 50 : taskJson.getInt("maxCtit");
        if (minCtit < 20) {
            minCtit = 20;
        }
        if (maxCtit < 50) {
            maxCtit = 50;
        }
        LogUtil.logApp("TaskInfo", MessageFormat.format("uuid:{0}, taskId:{1}, minCtit:{2}, maxCtit:{3}", uuid, String.valueOf(taskId), minCtit, maxCtit));

        // 过滤黑、白名单的gaid
        String wbGaidOrderId = taskJson.get("gaidBlackWhiteOrderId") == JSONObject.NULL ? "" : String.valueOf(taskJson.getInt("gaidBlackWhiteOrderId"));
        // 是否获取真实设备
        boolean useRealDevice = taskJson.getInt("isGetRealDevice") == 1;
        if (useRealDevice) {
            LogUtil.logApp("DeviceInfo", uuid + "使用真实设备");
        }
        // 获取设备信息
        String deviceStr = "";
        deviceStr = Device.getAndroidDeviceInfo(uuid, taskId, packageName, useRealDevice, wbGaidOrderId);
        JSONObject respJSON = new JSONObject(deviceStr);
        int respCode = respJSON.getInt("code");
        if (respCode != 200) {
            LogUtil.logApp("DeviceInfo Exception", "uuid:" + uuid + ", Device fetch error!");
            return;
        }
        JSONObject deviceJson = respJSON.getJSONObject("data");
        JSONObject gaJson = deviceJson.getJSONObject("gaInfo");
        String gaid = gaJson.getString("gaId");
        String ua = gaJson.getString("ua");
        String mnc = gaJson.optString("mnc", "001");
        mnc = fix2ThreeChars(mnc);
        gaJson.put("mnc", mnc);
        if (TextUtils.isEmpty(gaid) || TextUtils.isEmpty(ua)) {
            LogUtil.logApp("TaskInfo Exception", "uuid:" + uuid + ", gaid or ua is empty, gaid:" + gaid + ", ua:" + ua);
            return;
        }

        // 请求事件：成功拿到任务、代理和设备信息
        report(gaid, taskId, 1);
        Main.req.getAndIncrement();

        // 获取代理
        ProxyUtil proxyUtil = new ProxyUtil(iso, ipProvider, ipWeight, true);

        long clickTimeStampMil;
        int count = 0;
        IpInfo.DataBean ipData;
        String redirectUrl = "";
        while (true) {
            LogUtil.logApp("Recreated", "recreated...");

            // 获取代理IP
            ipData = proxyUtil.getIp(uuid);
            if (ipData == null) {
                LogUtil.logApp("ProxyInfo Exception", "uuid:" + uuid + ", ipData is null");
                return;
            }
            LogUtil.logApp("ProxyIpInfo", ipData.toString());

            clickTimeStampMil = System.currentTimeMillis();
            try {
                Map<String, String> header = new HashMap<>();
                LogUtil.logApp("DeviceInfo", "ua:" + ua);
                header.put("User-Agent", ua);

                String url = clickURL.replace("{aff_click_id}", uuid);
                url = url.replace("{gaid}", gaid);
                url = url.replace("{TID}", clickId);
                url = url.replace("{click_id}", clickId);

                String ip = ipData.getIp();
                if (ip != null) {
                    url = url.replace("{ip}", ip);
                }

                if (count++ == 0) {
                    // 点击事件：有代理才去点击
                    report(gaid, taskId, 3);
                }

                // Mock Click
                redirectUrl = click(uuid, taskId, gaid, url, header, ipData, campaign, adgroup, creative, callback);

                LogUtil.logApp("RedirectUrl", "uuid:" + uuid + ", 重定向URL:" + redirectUrl);
                if (!redirectUrl.isEmpty()) {
                    LogUtil.logApp("RedirectUrl", "重定向URL非空");
                    break;
                } else {
                    LogUtil.logApp("RedirectUrl", "重定向URL为空");
                }

                if (count > 5) {
                    break;
                }

            } catch (Exception e) {
                LogUtil.logApp("Redirect Exception", "uuid:" + uuid + ", clickURL:" + clickURL + ", Exception Message:" + e.getMessage());
                e.printStackTrace();
                continue;
            }
            break;
        }

        String msgDetail = ", Android, uuid:" + uuid + ", taskId:" + taskId + ", gaid:" + gaid;
        LogUtil.logApp("Redirect跳转结束", "uuid:" + uuid + ", url302:" + redirectUrl);

        String tag1 = ".apk";
        String[] tags = {
                "referrer=", ".apk?", "play.google.com",
                "apkpure.net", "com.playmate.playzone", "gochatapp.net", "m.fomo7.com", "lv77.co"
        };
        if (filterUrl(redirectUrl, tags) || redirectUrl.endsWith(tag1)) {
            LogUtil.logApp("Mock Download", "uuid:" + uuid + ", Redirect Succeed" + msgDetail + ", url302:" + redirectUrl);
            report(gaid, taskJson.getInt("id"), 4); // 下载事件：成功跳转到下载页面

            long rClickServerMil = System.currentTimeMillis() + RandomUtil.getRange(2100, 3000);
            long installBeginServerMil = rClickServerMil + RandomUtil.getRange(1500, 1500);
            long rClickMil = installBeginServerMil + RandomUtil.getRange(1500, 1000);
            long installBeginMil = rClickMil + RandomUtil.getRange(3000, 1000);
            long firstInstallTime = installBeginMil + RandomUtil.getRange(8 * 1000, 18 * 1000);

            // CTIT(click-to-install-time)
            int randomSleepTime;
            randomSleepTime = RandomUtil.getRange(minCtit * 1000, (maxCtit - minCtit) * 1000);

            long minEventTime = firstInstallTime + randomSleepTime;
            LogUtil.logApp("Mock Download", "uuid:" + uuid + ", randomSleepTime:" + randomSleepTime + ", minEventTime:" + minEventTime);

            // 提取referrer
            String referrerValue = HttpClientUtil.getReferrerValue(uuid, redirectUrl);
            ReferrerDetails referrerDetails = new ReferrerDetails(referrerValue, rClickMil / 1000, installBeginMil / 1000, false, rClickServerMil / 1000, installBeginServerMil / 1000, appJson.getString("versionName"));

            // launchInstall
            while (true) {
                LogUtil.log("Mock Install", "into launchInstall");
                if (System.currentTimeMillis() > minEventTime) {
                    LogUtil.logApp("Mock Install", "Installed Succeed" + msgDetail);

                    Machine machine = Machine.parseMachine(deviceJson, appJson, iso);
                    PackageBuilder packageBuilder = machine.createPackageBuilder(iso);
                    packageBuilder.setAlgorithm(algorithm);
                    packageBuilder.setSignatureMeta(signatureMeta);
                    packageBuilder.setProgramme(programme);
                    packageBuilder.setAdjustAlgoType(way);
                    packageBuilder.updateCreateTime(System.currentTimeMillis());

                    // 1：上报Session给adjust
                    LogUtil.logApp("Mock Install", "Ready to upload session to adjust");
                    ActivityPackage sessionPackage = packageBuilder.buildSessionPackage(uuid, way);
//                    post2Adjust(sessionPackage, ipData, iso, ua, uuid);
                    Map<String, Object> sessionDetailsMap = post2AdjustByHttpClient(sessionPackage, ipData, iso, ua, uuid);

                    // 2：上报Click给adjust
                    packageBuilder.setReferrerDetails(referrerDetails);
                    packageBuilder.updateCreateTime(System.currentTimeMillis());
                    LogUtil.logApp("Mock Install", "Ready to upload click to adjust");
                    ActivityPackage clickPackage = packageBuilder.buildClickPackage("install_referrer", uuid, way);
//                    post2Adjust(clickPackage, ipData, iso, ua, uuid);
                    post2AdjustByHttpClient(clickPackage, ipData, iso, ua, uuid);

                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    // 3：从adjust获取归因结果
                    packageBuilder.updateCreateTime(System.currentTimeMillis());
                    String initiatedBy = "backend";
                    if (sessionDetailsMap != null) {
                        if (sessionDetailsMap.get("result").toString().contains("ask_in")) {
                            initiatedBy = "sdk";
                        }
                    }
                    ActivityPackage attributionPackage = packageBuilder.buildAttributionPackage(initiatedBy, uuid, way);
                    LogUtil.logApp("Mock Install", "Ready to get attribution from adjust");
                    CloseableHttpResponse response = getFromAdjustByHttpClient(attributionPackage, ipData, iso, ua, uuid);
                    estimateAdjustAttribution2(response, sessionDetailsMap, keeps, uuid, gaid, taskId, packageName);
                    break;
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else {
            LogUtil.logApp("Mock Download", "Failure" + msgDetail + ", No features found in the url302:" + redirectUrl);
        }
    }

    public static String fix2ThreeChars(String str) {
        if (str == null) {
            return "";
        }
        if (str.length() == 1) {
            return "00" + str;
        }
        if (str.length() == 2) {
            return "0" + str;
        }
        return str;
    }

    /**
     * 上报事件
     * 0 当天限量
     * 1 请求量
     * 2 展现量
     * 3 点击量
     * 4 下载量
     * 5 安装量
     * 6 激活量
     *
     * @param gaid   gaid
     * @param taskId 任务id
     * @param event  事件类型
     */
    public static void report(String gaid, int taskId, int event) {
        String url = Constant.BASE_URL + "/oneselfPb?gaid=" + gaid + "&taskId=" + taskId + "&event=" + event;

        Downloader.doDownload(url, "GET", "", null);
        LogUtil.logApp("ReportEvent", "EventType:" + event + ", Url:" + url);
    }

    private static String click(String url, Map header, IpInfo.DataBean ipData, StringBuffer referrer) throws IOException {
        return Downloader.doDownloadRedirect(url, "GET", null, header, ipData, referrer);
    }

    private static String click(String url, Map header, IpInfo.DataBean ipData, StringBuffer referrer, List<String> url302List) throws IOException {
        return Downloader.doDownloadRedirect(url, "GET", null, header, ipData, referrer, url302List);
    }

    private static String click(String uuid, int taskId, String url, Map header, IpInfo.DataBean ipData, StringBuffer referrer, List<String> url302List) throws IOException {
        return Downloader.doDownloadRedirect(uuid, taskId, url, "GET", null, header, ipData, referrer, url302List);
    }

    private static String click(String uuid, int taskId, String gaid, String url, Map<String, String> headers, IpInfo.DataBean ipData, String campaign, String adgroup, String creative, String callback) throws IOException {
        return HttpClientUtil.getReferrerNew(uuid, taskId, gaid, url, null, headers, ipData, campaign, adgroup, creative, callback);
    }

    private static ResponseData post2Adjust(ActivityPackage activityPackage, IpInfo.DataBean ipData, String iso, String ua, String uuid) {
        String url = Constant.ADJUST_URL + activityPackage.getPath();

        try {
            ResponseData responseData = UtilNetworking.createPOSTHttpsURLConnection(url, activityPackage, -1, ipData, iso, ua);
            LogUtil.logApp("post2Adjust-responseData", "uuid:" + uuid + ", " + activityPackage.getPath() + ":" + responseData.toString());
            return responseData;
        } catch (Exception e) {
            LogUtil.logApp("post2Adjust-responseData Exception", e.getMessage());
            e.printStackTrace();
        }

        return null;
    }

    private static ResponseData getFromAdjust(ActivityPackage activityPackage, String basePath, String baseUrl, IpInfo.DataBean ipData, String iso, String ua, String uuid) {
        try {
            // 获取归因结果
            ResponseData responseData = UtilNetworking.createGETHttpsURLConnection(activityPackage, basePath, baseUrl, ipData, iso, ua);
            LogUtil.logApp("getFromAdjust-responseData", "uuid:" + uuid + ", " + activityPackage.getPath() + ":" + responseData.toString());
            return responseData;
        } catch (Exception e) {
            LogUtil.logApp("getFromAdjust-responseData Exception", e.getMessage());
            e.printStackTrace();
        }

        return null;
    }

    private static boolean estimateAdjustAttribution(ResponseData response, String uuid, String gaid, int taskId, String packageName) {

        String taskDetail = ", uuid:" + uuid + ", taskId:" + taskId + ", packageName:" + packageName + ", gaid:" + gaid;

        if (response == null) {
            LogUtil.logApp("Adjust Attribution Error", "adjust归因response为空" + taskDetail);
            return false;
        }

        try {
            LogUtil.logApp("Adjust Attribution", "response:" + response + taskDetail);
            if (!response.success) {
                LogUtil.logApp("Adjust Attribution Error", "请求adjust归因结果失败" + taskDetail);
                return false;
            }

            JSONObject jsonResponse = response.jsonResponse;
            JSONObject attributionJson = jsonResponse.getJSONObject("attribution");
            if (attributionJson == null) {
                LogUtil.logApp("Adjust Attribution Error", "adjust归因attributionJson为空" + taskDetail);
                return false;
            }

            String tracker_name = attributionJson.getString("tracker_name");
            if (tracker_name != null && !"Organic".equals(tracker_name) && !"No User Consent".equals(tracker_name)) {
                LogUtil.logApp("Adjust Attribution", "归因成功" + taskDetail);
                report(gaid, taskId, 5); // 安装事件：归因成功才计算一个
                return true;
            } else {
                LogUtil.logApp("Adjust Attribution", "归因失败" + taskDetail);
            }
        } catch (Exception e) {
            LogUtil.logApp("Adjust Attribution Exception", "识别归因结果发生异常：" + response + ", TaskDetail:" + taskDetail);
            e.printStackTrace();
        }

        return false;
    }


    public static byte[] xor(byte[] data, String key) {
        int len = data.length;
        int lenKey = key.length();
        int i = 0;
        int j = 0;
        while (i < len) {
            if (j >= lenKey) {
                j = 0;
            }
            data[i] = (byte) (data[i] ^ key.charAt(j));
            i++;
            j++;
        }
        return data;
    }

    private static Map<String, Object> post2AdjustByHttpClient(ActivityPackage activityPackage, IpInfo.DataBean ipData, String iso, String ua, String uuid) {
        String url = Constant.ADJUST_URL + activityPackage.getPath();

        try {
            LogUtil.logApp("post2AdjustByHttpClient", "uuid:" + uuid + ", url:" + url);
            Map<String, Object> reqDetails = UtilNetworking.createPOSTHttpsURLConnection2(uuid, url, activityPackage, ipData, iso, ua);
            LogUtil.logApp("post2Adjust-responseData", "uuid:" + uuid + ", " + activityPackage.getPath() + ":" + reqDetails.get("result").toString());
            return reqDetails;
        } catch (Exception e) {
            LogUtil.logApp("post2Adjust-responseData Exception", e.getMessage());
            e.printStackTrace();
        }

        return null;
    }

    private static CloseableHttpResponse getFromAdjustByHttpClient(ActivityPackage activityPackage, IpInfo.DataBean ipData, String iso, String ua, String uuid) {
        // 需要加一个/
        String url = Constant.ADJUST_URL + "/" + activityPackage.getPath();

        CloseableHttpResponse response = null;

        try {
            // 获取归因结果
            LogUtil.logApp("getFromAdjustByHttpClient", "uuid:" + uuid + ", url:" + url);
            response = UtilNetworking.createGETHttpsURLConnection2(uuid, activityPackage, url, ipData, iso, ua);
        } catch (Exception e) {
            LogUtil.logApp("getFromAdjust-responseData Exception", e.getMessage());
            e.printStackTrace();
        }

        return response;
    }

    private static boolean estimateAdjustAttribution2(CloseableHttpResponse response, Map<String, Object> sessionDetailsMap, String keeps, String uuid, String gaid, int taskId, String packageName) {
        String taskDetail = ", uuid:" + uuid + ", taskId:" + taskId + ", packageName:" + packageName + ", gaid:" + gaid;
        String attributionResult = "";

        String invalidSign = "Invalid Signature";
        String untrustedDevices = "Untrusted Devices";

        try {
            // 获取归因结果
            attributionResult = EntityUtils.toString(response.getEntity(), "UTF-8");
            if (attributionResult.isEmpty()) {
                LogUtil.logApp("Adjust Attribution Error", "adjust归因response为空" + taskDetail);
                return false;
            }

            LogUtil.logApp("Adjust Attribution", "Attribution Response:" + attributionResult + taskDetail);
            JSONObject jsonResponse = new JSONObject(attributionResult);
            if (jsonResponse.has("attribution")) {
                JSONObject attributionJson = jsonResponse.getJSONObject("attribution");
                String tracker_name = attributionJson.getString("tracker_name");
                if (tracker_name != null && !"Organic".equals(tracker_name) && !tracker_name.contains(invalidSign) && !tracker_name.contains(untrustedDevices)) {
                    LogUtil.logApp("Adjust Attribution", "归因成功" + taskDetail);
                    // 安装事件：归因成功才计算一个
                    report(gaid, taskId, 5);
                    // 上传留存信息
                    if (!TextUtils.isEmpty(keeps)) {
                        LogUtil.logApp("Adjust Attribution", "需要上报留存数据：" + taskDetail);
                        reportSessionInfo2Server(uuid, taskId, sessionDetailsMap);
                    }
                    return true;
                } else {
                    LogUtil.logApp("Adjust Attribution", "归因失败" + taskDetail);
                }
            } else {
                LogUtil.logApp("Adjust Attribution", "归因失败, 响应结果没有特定Key (attribution), " + jsonResponse + taskDetail);
                if (jsonResponse.has("error")) {
                    if (jsonResponse.getString("error").contains("Data residency policy violation")) {
                        LogUtil.logApp("Adjust Attribution", "归因失败, Data residency policy violation" + taskDetail);
                    }
                }
                return false;
            }
        } catch (Exception e) {
            LogUtil.logApp("Adjust Attribution Exception", "识别归因结果发生异常：" + attributionResult + ", TaskDetail:" + taskDetail);
            e.printStackTrace();
        }

        return false;
    }

    private static void reportSessionInfo2Server(String uuid, int taskId, Map<String, Object> sessionParamsMap) {
        Map<String, String> params;
        Map<String, String> headers;

        try {
            headers = (Map<String, String>) sessionParamsMap.get("headers");
            params = (Map<String, String>) sessionParamsMap.get("params");

            params.put("Client-SDK", headers.get("Client-SDK"));
            params.put("User-Agent", headers.get("User-Agent"));

            String paramsString = params.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining("&"));

            JSONObject dataJson = new JSONObject();
            dataJson.put("taskId", String.valueOf(taskId));
            dataJson.put("data", paramsString);

            LogUtil.logApp("reportSession", "uuid:" + uuid + ", 留存参数：" + dataJson.toString());

            String result = HttpClientUtil.doPostRawData(ALIVE_URL, dataJson.toString());
            if ("ok".equals(result)) {
                LogUtil.logApp("reportSession", "uuid:" + uuid + ", 留存数据上报成功");
            } else {
                LogUtil.logApp("reportSession", "uuid:" + uuid + ", 留存数据上报失败，" + result);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static boolean filterUrl(String url, String[] tags) {
        for (String tag : tags) {
            if (url.contains(tag)) {
                return true;
            }
        }
        return false;
    }
}
